<?php
if(!class_exists('XARQuery', false)){
	require_once(XFRAMEWORK_DIR.'/module/active_record/XARQuery.php');
}
class XARShardQuery extends XARQuery{
	protected $shardId = null;
	protected $size = null;
	protected $offset = null;
	
	public function __construct($modelName){
		if(!XClassLoader::loader($modelName)->isSubclassOf('XARShardRecord')){
			throw new XORMException("class {$modelName} does not exist or is not a sub class of XARPartitionRecord");
		}
		$this->dao = XARShardDAO::dao($modelName);
	}

	/**
	 * @return XARQuery
	 */		
	public function table($table){
		if(is_string($table)){
			return parent::table($this->dao()->loadTableByShardName($table));
		}
		return parent::table($table);
	}	
	
	protected function _offset(){
		if(!is_null($this->offset)){
			return $this->offset;
		}
		$projections = $this->criteria()->projections();
		if(isset($projections[OFFSET])){
			$this->offset = $projections[OFFSET]->value();
		}else{
			$this->offset = 0;
		}
		return $this->offset;
	}
	
	protected function _size(){
		if(!is_null($this->size)){
			return $this->size;
		}
		$projections = $this->criteria()->projections();
		if(isset($projections[SIZE])){
			$this->size = $projections[SIZE]->value();
		}else{
			$this->size = 0;
		}
		return $this->size;		
	}
	
	/**
	 * @return XARQuery
	 */	
	public static function get($modelName){
		$loader = XClassLoader::loader($modelName);
		if(!$loader->isSubclassOf('XARRecord')){
			throw new XORMException("class {$modelName} does not exist or is not a sub class of XARRecord");
		}
		if(!$loader->isSubclassOf('XARShardRecord')){
			return new XARQuery($modelName);
		}
		return new self($modelName);
	}
	
	/**
	 * @return XARShardQuery
	 */	
	public function shardId($shardId){
		return $this->eq($this->dao()->shardId(), $shardId);
	}
	
	/**
	 * @return XARShardQuery
	 */	
	public function uniqueId($uniqueId){
		return $this->shardId($uniqueId);
	}
	
	/**
	 * @return XARShardQuery
	 */	
	public function eq($key, $value){
		if($key == $this->dao()->shardId()){
			$this->shardId = $value;
		}
		return parent::eq($key, $value);
	}
	
	/**
	 * @return XARShardQuery
	 */	
	public function in($key, array $array){
		if($key == $this->dao()->shardId()){
			$this->shardId = $array;
		}
		return parent::in($key, $array);
	}
	
	/**
	 * @return XModel
	 */	
	public function find($PK = null, $asArray = false){
		if(!is_null($this->table)){
			return parent::find($PK, $asArray);
		}		
		if(!is_null($PK)){
			$this->shardId($PK);
		}
		if(is_null($this->shardId)){
			$shards = $this->dao()->allShards();
			foreach($shards as $shard){		
				$result = $this->cloneQuery()
					->table($shard)
					->find(null, $asArray);
				if($result){
					return $result;
				}
			}	
		}
		if(is_array($this->shardId)){
			foreach($this->shardId as $shardId){				
				$result = $this->cloneQuery()
					->table($this->dao()->loadTableByShardId($shardId))
					->shardId($shardId)
					->find(null, $asArray);
				if($result){
					return $result;
				}
			}
		}
		return $this->table($this->dao()->loadTableByShardId($this->shardId))
			->find(null, $asArray);
	}
	
	/**
	 * @return array
	 */		
	public function findAll($asArray = false){
		if(!is_null($this->table)){
			return parent::findAll($asArray);
		}
		$this->_offset();
		$this->_size();
		if(is_null($this->shardId)){
			$shards = $this->dao()->allShards();
			$traverser = new XTableTraverser($this, $shards);
			return $traverser->traverse($this->offset, $this->size, $asArray);
		}
		if(is_array($this->shardId)){
			$tables = array();
			foreach($this->shardId as $shardId){	
				$this->dao()->loadTableByShardId($shardId);
				$table = $this->dao()->driver()->name();	
				$tables[$table] = $table;
			}
			ksort($tables);
			$traverser = new XTableTraverser($this, $tables);
			return $traverser->traverse($this->offset, $this->size, $asArray);		
		}
		return $this->table($this->dao()->loadTableByShardId($this->shardId))
			->findAll($asArray);
	}
	
	/**
	 * @return int
	 */	
	public function count($field = null){
		return $this->aggregate('count', $field);
	}
	
	/**
	 * @return int
	 */
	public function sum($field){
		return $this->aggregate('sum', $field);
	}
	
	/**
	 * @return int
	 */
	public function avg($field){
		$countQuery = $this->cloneQuery();
		return $this->sum($field) / $countQuery->count($field);
	}
	
	protected function aggregate($aggregate, $field = null){
		if(!is_null($this->table)){
			return parent::$aggregate($field);
		}
		if(is_null($this->shardId)){
			$shards = $this->dao()->allShards();
			$traverser = new XTableTraverser($this, $shards);
			return $traverser->aggregate($aggregate, $field);
		}
		if(is_array($this->shardId)){
			$shards = array();
			foreach($this->shardId as $shardId){	
				$this->dao()->loadTableByShardId($shardId);
				$shard = $this->dao()->driver()->name();
				$shards[$shard] = $shard;
			}
			$traverser = new XTableTraverser($this, $shards);
			return $traverser->aggregate($aggregate, $field);
		}
		return $this->cloneQuery()
			->table($this->dao()->loadTableByShardId($this->shardId))
			->$aggregate($field);
	}
}
?>